<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> 
    <title>深入剖析Redis客户端Jedis的特性和原理 - 天地维杰网</title>
    <meta name="keywords" content="系统架构,shutdown,不与天斗,Domino,博客,程序员,架构师,笔记,技术,分享,java,Redis">
    
    <meta property="og:title" content="深入剖析Redis客户端Jedis的特性和原理">
    <meta property="og:site_name" content="天地维杰网">
    <meta property="og:image" content="/img/author.jpg"> 
    <meta name="title" content="深入剖析Redis客户端Jedis的特性和原理 - 天地维杰网" />
    <meta name="description" content="天地维杰网 | 博客 | 软件 | 架构 | Java "> 
    <link rel="shortcut icon" href="http://www.shutdown.cn/img/favicon.ico" />
    <link rel="apple-touch-icon" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link rel="apple-touch-icon-precomposed" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link href="http://www.shutdown.cn/js/vendor/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/main.css" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/syntax.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" id="hexo.configuration">
  var NexT = window.NexT || {};
  var CONFIG = {
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"post"},
     fancybox: true, 
    motion: true
  };
</script>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container one-collumn sidebar-position-left page-home  ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"> <div class="site-meta  custom-logo ">

  <div class="custom-logo-site-title">
    <a href="http://www.shutdown.cn"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">天地维杰网</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle">人如秋鸿来有信，事若春梦了无痕</p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
    <ul id="menu" class="menu">
      
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-mac"></i> <br />Mac相关
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-spring"></i> <br />Spring和Springboot
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/daily/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-problem"></i> <br />日常问题
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/java/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-coffee"></i> <br />java
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jvm/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-java"></i> <br />JVM
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/linux/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-linux"></i> <br />linux
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/redis/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-full"></i> <br />Redis
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jvm/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-server"></i> <br />中间件
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/front/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-front"></i> <br />前端
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/post/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/about/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于
          </a>
        </li>
      
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger"> <i class="menu-item-icon fa fa-search fa-fw"></i> <br /> 搜索</a>
      </li>
    </ul>
    <div class="site-search">
      <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>

    </div>
</nav>

 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
<section id="posts" class="posts-expand">
  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <header class="post-header">
      <h1 class="post-title" itemprop="name headline">
        <a class="post-title-link" href="http://www.shutdown.cn/post/redis/" itemprop="url">
        深入剖析Redis客户端Jedis的特性和原理
        </a>
      </h1>
      <div class="post-meta">
      <span class="post-time">
<span class="post-meta-item-icon">
    <i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">时间：</span>
<time itemprop="dateCreated" datetime="2016-03-22T13:04:35+08:00" content="0001-01-01">
    0001-01-01
</time>
</span> 
      
       <span>
&nbsp; | &nbsp;
<span class="post-meta-item-icon">
    <i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读：</span>
<span class="leancloud-visitors-count">8681 字 ~18分钟</span>
</span>
      </div>
    </header>
    <div class="post-body" itemprop="articleBody">
    <h1 id="深入剖析redis客户端jedis的特性和原理">深入剖析Redis客户端Jedis的特性和原理</h1>
<h2 id="一开篇">一、开篇</h2>
<p>Redis作为目前通用的缓存选型，因其高性能而倍受欢迎。Redis的2.x版本仅支持单机模式，从3.0版本开始引入集群模式。</p>
<p>Redis的Java生态的客户端当中包含Jedis、Redisson、Lettuce，不同的客户端具备不同的能力是使用方式，本文主要分析Jedis客户端。</p>
<p>Jedis客户端同时支持单机模式、分片模式、集群模式的访问模式，通过构建Jedis类对象实现单机模式下的数据访问，通过构建ShardedJedis类对象实现分片模式的数据访问，通过构建JedisCluster类对象实现集群模式下的数据访问。</p>
<p>Jedis客户端支持单命令和Pipeline方式访问Redis集群，通过Pipeline的方式能够提高集群访问的效率。</p>
<p>本文的整体分析基于Jedis的3.5.0版本进行分析，相关源码均参考此版本。</p>
<h2 id="二jedis访问模式对比">二、Jedis访问模式对比</h2>
<p>Jedis客户端操作Redis主要分为三种模式，分表是单机模式、分片模式、集群模式。</p>
<ul>
<li><strong>单机模式</strong>主要是创建Jedis对象来操作单节点的Redis，只适用于访问单个Redis节点。</li>
<li><strong>分片模式</strong>（ShardedJedis）主要是通过创建ShardedJedisPool对象来访问分片模式的多个Redis节点，是Redis没有集群功能之前客户端实现的一个数据分布式方案，本质上是客户端通过一致性哈希来实现数据分布式存储。</li>
<li><strong>集群模式</strong>（JedisCluster）主要是通过创建JedisCluster对象来访问集群模式下的多个Redis节点，是Redis3.0引入集群模式后客户端实现的集群访问访问，本质上是通过引入槽（slot）概念以及通过CRC16哈希槽算法来实现数据分布式存储。</li>
</ul>
<p>单机模式不涉及任何分片的思想，所以我们着重分析<strong>分片模式</strong>和<strong>集群模式</strong>的理念。</p>
<h3 id="21-分片模式">2.1 分片模式</h3>
<ul>
<li>分片模式本质属于基于客户端的分片，在客户端实现如何根据一个key找到Redis集群中对应的节点的方案。</li>
<li>Jedis的客户端分片模式采用一致性Hash来实现，一致性Hash算法的好处是当Redis节点进行增减时只会影响新增或删除节点前后的小部分数据，相对于取模等算法来说对数据的影响范围较小。</li>
<li>Redis在大部分场景下作为缓存进行使用，所以不用考虑数据丢失致使缓存穿透造成的影响，在Redis节点增减时可以不用考虑部分数据无法命中的问题。</li>
</ul>
<p>分片模式的整体应用如下图所示，核心在于客户端的一致性Hash策略。</p>
<!-- raw HTML omitted -->
<p>（引用自：www.cnblogs.com）</p>
<h3 id="22-集群模式">2.2 集群模式</h3>
<ul>
<li>集群模式本质属于服务器分片技术，由Redis集群本身提供分片功能，从Redis 3.0版本开始正式提供。</li>
<li>集群的原理是：一个 Redis 集群包含16384 个哈希槽（Hash slot）， Redis保存的每个键都属于这16384个哈希槽的其中一个， 集群使用公式CRC16(key)%16384 来计算键 key 属于哪个槽， 其中 CRC16(key) 语句用于计算键key的CRC16校验和 。</li>
<li>集群中的每个节点负责处理一部分哈希槽。举个例子， 一个集群可以有三个哈希槽， 其中：</li>
</ul>
<blockquote>
<p>节点 A 负责处理 0 号至 5500 号哈希槽。</p>
<p>节点 B 负责处理 5501 号至 11000 号哈希槽。</p>
<p>节点 C 负责处理 11001 号至 16383 号哈希槽。</p>
</blockquote>
<ul>
<li>Redis在集群模式下对于key的读写过程首先将对应的key值进行CRC16计算得到对应的哈希值，将哈希值对槽位总数取模映射到对应的槽位，最终映射到对应的节点进行读写。以命令set(&ldquo;key&rdquo;, &ldquo;value&rdquo;)为例子，它会使用CRC16算法对key进行计算得到哈希值28989，然后对16384进行取模得到12605，最后找到12605对应的Redis节点，最终跳转到该节点执行set命令。</li>
<li>集群模式的整体应用如下图所示，核心在于集群哈希槽的设计以及重定向命令。</li>
</ul>
<!-- raw HTML omitted -->
<p>（引用自：https://www.jianshu.com）</p>
<p>三、Jedis的基础用法</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#75715e">// Jedis单机模式的访问
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span><span style="color:#f92672">(</span>String<span style="color:#f92672">[</span><span style="color:#f92672">]</span> args<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 创建Jedis对象
</span><span style="color:#75715e"></span>    jedis <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Jedis<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;localhost&#34;</span><span style="color:#f92672">,</span> 6379<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 执行hmget操作
</span><span style="color:#75715e"></span>    jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">hmget</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;foobar&#34;</span><span style="color:#f92672">,</span> <span style="color:#e6db74">&#34;foo&#34;</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 关闭Jedis对象
</span><span style="color:#75715e"></span>    jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">close</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
<span style="color:#f92672">}</span>
 
<span style="color:#75715e">// Jedis分片模式的访问
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span><span style="color:#f92672">(</span>String<span style="color:#f92672">[</span><span style="color:#f92672">]</span> args<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    HostAndPort redis1 <span style="color:#f92672">=</span> HostAndPortUtil<span style="color:#f92672">.</span><span style="color:#a6e22e">getRedisServers</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>0<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    HostAndPort redis2 <span style="color:#f92672">=</span> HostAndPortUtil<span style="color:#f92672">.</span><span style="color:#a6e22e">getRedisServers</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>1<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    List<span style="color:#f92672">&lt;</span>JedisShardInfo<span style="color:#f92672">&gt;</span> shards <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ArrayList<span style="color:#f92672">&lt;</span>JedisShardInfo<span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span>2<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    JedisShardInfo shard1 <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> JedisShardInfo<span style="color:#f92672">(</span>redis1<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    JedisShardInfo shard2 <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> JedisShardInfo<span style="color:#f92672">(</span>redis2<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 创建ShardedJedis对象
</span><span style="color:#75715e"></span>    ShardedJedis shardedJedis <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ShardedJedis<span style="color:#f92672">(</span>shards<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 通过ShardedJedis对象执行set操作
</span><span style="color:#75715e"></span>    shardedJedis<span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;a&#34;</span><span style="color:#f92672">,</span> <span style="color:#e6db74">&#34;bar&#34;</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
<span style="color:#f92672">}</span>
 
<span style="color:#75715e">// Jedis集群模式的访问
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span><span style="color:#f92672">(</span>String<span style="color:#f92672">[</span><span style="color:#f92672">]</span> args<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 构建redis的集群池
</span><span style="color:#75715e"></span>    Set<span style="color:#f92672">&lt;</span>HostAndPort<span style="color:#f92672">&gt;</span> nodes <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashSet<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">add</span><span style="color:#f92672">(</span><span style="color:#66d9ef">new</span> HostAndPort<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;127.0.0.1&#34;</span><span style="color:#f92672">,</span> 7001<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">add</span><span style="color:#f92672">(</span><span style="color:#66d9ef">new</span> HostAndPort<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;127.0.0.1&#34;</span><span style="color:#f92672">,</span> 7002<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">add</span><span style="color:#f92672">(</span><span style="color:#66d9ef">new</span> HostAndPort<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;127.0.0.1&#34;</span><span style="color:#f92672">,</span> 7003<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    <span style="color:#75715e">// 创建JedisCluster
</span><span style="color:#75715e"></span>    JedisCluster cluster <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> JedisCluster<span style="color:#f92672">(</span>nodes<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    <span style="color:#75715e">// 执行JedisCluster对象中的方法
</span><span style="color:#75715e"></span>    cluster<span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;cluster-test&#34;</span><span style="color:#f92672">,</span> <span style="color:#e6db74">&#34;my jedis cluster test&#34;</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    String result <span style="color:#f92672">=</span> cluster<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;cluster-test&#34;</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>Jedis通过创建Jedis的类对象来实现单机模式下的数据访问，通过构建JedisCluster类对象来实现集群模式下的数据访问。</p>
<p>要理解Jedis的访问Redis的整个过程，可以通过先理解单机模式下的访问流程，在这个基础上再分析集群模式的访问流程会比较合适。</p>
<p>四、Jedis单机模式的访问</p>
<p>Jedis访问单机模式Redis的整体流程图如下所示，从图中可以看出核心的流程包含Jedis对象的创建以及通过Jedis对象实现Redis的访问。</p>
<p>熟悉Jedis访问单机Redis的过程，本身就是需要了解Jedis的创建过程以及执行Redis命令的过程。</p>
<ul>
<li>Jedis的创建过程核心在于创建Jedis对象以及Jedis内部变量Client对象。</li>
<li>Jedis访问Redis的过程在于通过Jedis内部的Client对象访问Redis。</li>
</ul>
<!-- raw HTML omitted -->
<h3 id="41-创建过程">4.1 创建过程</h3>
<p>Jedis本身的类关系图如下图所示，从图中我们能够看到Jedis继承自BinaryJedis类。</p>
<p>在BinaryJedis类中存在和Redis对接的Client类对象，Jedis通过父类的BinaryJedis的Client对象实现Redis的读写。</p>
<!-- raw HTML omitted -->
<p>Jedis类在创建过程中通过父类BinaryJedis创建了Client对象，而了解Client对象是进一步理解访问过程的关键。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Jedis</span> <span style="color:#66d9ef">extends</span> BinaryJedis <span style="color:#66d9ef">implements</span> JedisCommands<span style="color:#f92672">,</span> MultiKeyCommands<span style="color:#f92672">,</span>
    AdvancedJedisCommands<span style="color:#f92672">,</span> ScriptingCommands<span style="color:#f92672">,</span> BasicCommands<span style="color:#f92672">,</span> ClusterCommands<span style="color:#f92672">,</span> SentinelCommands<span style="color:#f92672">,</span> ModuleCommands <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">protected</span> JedisPoolAbstract dataSource <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Jedis</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String host<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> port<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 创建父类BinaryJedis对象
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span><span style="color:#f92672">(</span>host<span style="color:#f92672">,</span> port<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BinaryJedis</span> <span style="color:#66d9ef">implements</span> BasicCommands<span style="color:#f92672">,</span> BinaryJedisCommands<span style="color:#f92672">,</span> MultiKeyBinaryCommands<span style="color:#f92672">,</span>
    AdvancedBinaryJedisCommands<span style="color:#f92672">,</span> BinaryScriptingCommands<span style="color:#f92672">,</span> Closeable <span style="color:#f92672">{</span>
 
  <span style="color:#75715e">// 访问redis的Client对象
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">protected</span> Client client <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">BinaryJedis</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String host<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> port<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 创建Client对象访问redis
</span><span style="color:#75715e"></span>    client <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Client<span style="color:#f92672">(</span>host<span style="color:#f92672">,</span> port<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>Client类的类关系图如下图所示，Client对象继承自BinaryClient和Connection类。在BinaryClient类中存在Redis访问密码等相关参数，在Connection类在存在访问Redis的socket对象以及对应的输入输出流。本质上Connection是和Redis进行通信的核心类。</p>
<!-- raw HTML omitted -->
<p>Client类在创建过程中初始化核心父类Connection对象，而Connection是负责和Redis直接进行通信。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java">
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Client</span> <span style="color:#66d9ef">extends</span> BinaryClient <span style="color:#66d9ef">implements</span> Commands <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Client</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String host<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> port<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">super</span><span style="color:#f92672">(</span>host<span style="color:#f92672">,</span> port<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BinaryClient</span> <span style="color:#66d9ef">extends</span> Connection <span style="color:#f92672">{</span>
  <span style="color:#75715e">// 存储和Redis连接的相关信息
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">boolean</span> isInMulti<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> String user<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> String password<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">int</span> db<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">boolean</span> isInWatch<span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">BinaryClient</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String host<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> port<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">super</span><span style="color:#f92672">(</span>host<span style="color:#f92672">,</span> port<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Connection</span> <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#75715e">// 管理和Redis连接的socket信息及对应的输入输出流
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> JedisSocketFactory jedisSocketFactory<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> Socket socket<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> RedisOutputStream outputStream<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> RedisInputStream inputStream<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">int</span> infiniteSoTimeout <span style="color:#f92672">=</span> 0<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">boolean</span> broken <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span><span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Connection</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String host<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> port<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">boolean</span> ssl<span style="color:#f92672">,</span>
      SSLSocketFactory sslSocketFactory<span style="color:#f92672">,</span> SSLParameters sslParameters<span style="color:#f92672">,</span>
      HostnameVerifier hostnameVerifier<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 构建DefaultJedisSocketFactory来创建和Redis连接的Socket对象
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span><span style="color:#f92672">(</span><span style="color:#66d9ef">new</span> DefaultJedisSocketFactory<span style="color:#f92672">(</span>host<span style="color:#f92672">,</span> port<span style="color:#f92672">,</span> Protocol<span style="color:#f92672">.</span><span style="color:#a6e22e">DEFAULT_TIMEOUT</span><span style="color:#f92672">,</span>
        Protocol<span style="color:#f92672">.</span><span style="color:#a6e22e">DEFAULT_TIMEOUT</span><span style="color:#f92672">,</span> ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h3 id="42-访问过程">4.2 访问过程</h3>
<p>以Jedis执行set命令为例，整个过程如下：</p>
<ul>
<li>Jedis的set操作是通过Client的set操作来实现的。</li>
<li>Client的set操作是通过父类Connection的sendCommand来实现。</li>
</ul>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java">
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Jedis</span> <span style="color:#66d9ef">extends</span> BinaryJedis <span style="color:#66d9ef">implements</span> JedisCommands<span style="color:#f92672">,</span> MultiKeyCommands<span style="color:#f92672">,</span>
    AdvancedJedisCommands<span style="color:#f92672">,</span> ScriptingCommands<span style="color:#f92672">,</span> BasicCommands<span style="color:#f92672">,</span> ClusterCommands<span style="color:#f92672">,</span> SentinelCommands<span style="color:#f92672">,</span> ModuleCommands <span style="color:#f92672">{</span>
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String key<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> String value<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    checkIsInMultiOrPipeline<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// client执行set操作
</span><span style="color:#75715e"></span>    client<span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span>key<span style="color:#f92672">,</span> value<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">return</span> client<span style="color:#f92672">.</span><span style="color:#a6e22e">getStatusCodeReply</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Client</span> <span style="color:#66d9ef">extends</span> BinaryClient <span style="color:#66d9ef">implements</span> Commands <span style="color:#f92672">{</span>
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String key<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> String value<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 执行set命令
</span><span style="color:#75715e"></span>    set<span style="color:#f92672">(</span>SafeEncoder<span style="color:#f92672">.</span><span style="color:#a6e22e">encode</span><span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">,</span> SafeEncoder<span style="color:#f92672">.</span><span style="color:#a6e22e">encode</span><span style="color:#f92672">(</span>value<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BinaryClient</span> <span style="color:#66d9ef">extends</span> Connection <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> <span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span> key<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span> value<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 发送set指令
</span><span style="color:#75715e"></span>    sendCommand<span style="color:#f92672">(</span>SET<span style="color:#f92672">,</span> key<span style="color:#f92672">,</span> value<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Connection</span> <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">sendCommand</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> ProtocolCommand cmd<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span><span style="color:#f92672">.</span><span style="color:#f92672">.</span><span style="color:#f92672">.</span> args<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// socket连接redis
</span><span style="color:#75715e"></span>      connect<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#75715e">// 按照redis的协议发送命令
</span><span style="color:#75715e"></span>      Protocol<span style="color:#f92672">.</span><span style="color:#a6e22e">sendCommand</span><span style="color:#f92672">(</span>outputStream<span style="color:#f92672">,</span> cmd<span style="color:#f92672">,</span> args<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisConnectionException ex<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h2 id="五jedis分片模式的访问">五、Jedis分片模式的访问</h2>
<p>基于前面已经介绍的Redis分片模式的一致性Hash的原理来理解Jedis的分片模式的访问。</p>
<p>关于Redis分片模式的概念：Redis在3.0版本之前没有集群模式的概念，这导致单节点能够存储的数据有限，通过Redis的客户端如Jedis在客户端通过一致性Hash算法来实现数据的分片存储。本质上Redis的分片模式跟Redis本身没有任何关系，只是通过客户端来解决单节点数据有限存储的问题。ShardedJedis访问Redis的核心在于构建对象的时候初始化一致性Hash对象，构建一致性Hash经典的Hash值和node的映射关系。构建完映射关系后执行set等操作就是Hash值到node的寻址过程，寻址完成后直接进行单节点的操作。</p>
<!-- raw HTML omitted -->
<h3 id="51-创建过程">5.1 创建过程</h3>
<p>ShardedJedis的创建过程在于父类的Sharded中关于一致性Hash相关的初始化过程，核心在于构建一致性的虚拟节点以及虚拟节点和Redis节点的映射关系。源码中最核心的部分代码在于根据根据权重映射成未160个虚拟节点，通过虚拟节点来定位到具体的Redis节点。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Sharded</span><span style="color:#f92672">&lt;</span>R<span style="color:#f92672">,</span> S <span style="color:#66d9ef">extends</span> ShardInfo<span style="color:#f92672">&lt;</span>R<span style="color:#f92672">&gt;</span><span style="color:#f92672">&gt;</span> <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> DEFAULT_WEIGHT <span style="color:#f92672">=</span> 1<span style="color:#f92672">;</span>
  <span style="color:#75715e">// 保存虚拟节点和redis的node节点的映射关系
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> TreeMap<span style="color:#f92672">&lt;</span>Long<span style="color:#f92672">,</span> S<span style="color:#f92672">&gt;</span> nodes<span style="color:#f92672">;</span>
  <span style="color:#75715e">// hash算法
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">final</span> Hashing algo<span style="color:#f92672">;</span>
  <span style="color:#75715e">// 保存redis节点和访问该节点的Jedis的连接信息
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">final</span> Map<span style="color:#f92672">&lt;</span>ShardInfo<span style="color:#f92672">&lt;</span>R<span style="color:#f92672">&gt;</span><span style="color:#f92672">,</span> R<span style="color:#f92672">&gt;</span> resources <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> LinkedHashMap<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Sharded</span><span style="color:#f92672">(</span>List<span style="color:#f92672">&lt;</span>S<span style="color:#f92672">&gt;</span> shards<span style="color:#f92672">,</span> Hashing algo<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">algo</span> <span style="color:#f92672">=</span> algo<span style="color:#f92672">;</span>
    initialize<span style="color:#f92672">(</span>shards<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
 
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">initialize</span><span style="color:#f92672">(</span>List<span style="color:#f92672">&lt;</span>S<span style="color:#f92672">&gt;</span> shards<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    nodes <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> TreeMap<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 遍历每个redis的节点并设置hash值到节点的映射关系
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> 0<span style="color:#f92672">;</span> i <span style="color:#f92672">!</span><span style="color:#f92672">=</span> shards<span style="color:#f92672">.</span><span style="color:#a6e22e">size</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span> <span style="color:#f92672">+</span><span style="color:#f92672">+</span>i<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">final</span> S shardInfo <span style="color:#f92672">=</span> shards<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>i<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#75715e">// 根据权重映射成未160个虚拟节点
</span><span style="color:#75715e"></span>      <span style="color:#66d9ef">int</span> N <span style="color:#f92672">=</span>  160 <span style="color:#f92672">*</span> shardInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">getWeight</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>shardInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">getName</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">=</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> n <span style="color:#f92672">=</span> 0<span style="color:#f92672">;</span> n <span style="color:#f92672">&lt;</span> N<span style="color:#f92672">;</span> n<span style="color:#f92672">+</span><span style="color:#f92672">+</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        <span style="color:#75715e">// 构建hash值和节点映射关系
</span><span style="color:#75715e"></span>        nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">put</span><span style="color:#f92672">(</span><span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">algo</span><span style="color:#f92672">.</span><span style="color:#a6e22e">hash</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;SHARD-&#34;</span> <span style="color:#f92672">+</span> i <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;-NODE-&#34;</span> <span style="color:#f92672">+</span> n<span style="color:#f92672">)</span><span style="color:#f92672">,</span> shardInfo<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
      <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> n <span style="color:#f92672">=</span> 0<span style="color:#f92672">;</span> n <span style="color:#f92672">&lt;</span> N<span style="color:#f92672">;</span> n<span style="color:#f92672">+</span><span style="color:#f92672">+</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">put</span><span style="color:#f92672">(</span><span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">algo</span><span style="color:#f92672">.</span><span style="color:#a6e22e">hash</span><span style="color:#f92672">(</span>shardInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">getName</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;*&#34;</span> <span style="color:#f92672">+</span> n<span style="color:#f92672">)</span><span style="color:#f92672">,</span> shardInfo<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
      <span style="color:#75715e">// 保存每个节点的访问对象
</span><span style="color:#75715e"></span>      resources<span style="color:#f92672">.</span><span style="color:#a6e22e">put</span><span style="color:#f92672">(</span>shardInfo<span style="color:#f92672">,</span> shardInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">createResource</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h3 id="52-访问过程">5.2 访问过程</h3>
<p>ShardedJedis的访问过程就是一致性Hash的计算过程，核心的逻辑就是：通过Hash算法对访问的key进行Hash计算生成Hash值，根据Hash值获取对应Redis节点，根据对应的Redis节点获取对应的访问对象Jedis。获取访问对象Jedis之后就可以直接进行命令操作。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Sharded</span><span style="color:#f92672">&lt;</span>R<span style="color:#f92672">,</span> S <span style="color:#66d9ef">extends</span> ShardInfo<span style="color:#f92672">&lt;</span>R<span style="color:#f92672">&gt;</span><span style="color:#f92672">&gt;</span> <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> DEFAULT_WEIGHT <span style="color:#f92672">=</span> 1<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> TreeMap<span style="color:#f92672">&lt;</span>Long<span style="color:#f92672">,</span> S<span style="color:#f92672">&gt;</span> nodes<span style="color:#f92672">;</span>
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">final</span> Hashing algo<span style="color:#f92672">;</span>
  <span style="color:#75715e">// 保存redis节点和访问该节点的Jedis的连接信息
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">final</span> Map<span style="color:#f92672">&lt;</span>ShardInfo<span style="color:#f92672">&lt;</span>R<span style="color:#f92672">&gt;</span><span style="color:#f92672">,</span> R<span style="color:#f92672">&gt;</span> resources <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> LinkedHashMap<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> R <span style="color:#a6e22e">getShard</span><span style="color:#f92672">(</span>String key<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 根据redis节点找到对应的访问对象Jedis
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> resources<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>getShardInfo<span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
 
  <span style="color:#66d9ef">public</span> S <span style="color:#a6e22e">getShardInfo</span><span style="color:#f92672">(</span>String key<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">return</span> getShardInfo<span style="color:#f92672">(</span>SafeEncoder<span style="color:#f92672">.</span><span style="color:#a6e22e">encode</span><span style="color:#f92672">(</span>getKeyTag<span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
 
  <span style="color:#66d9ef">public</span> S <span style="color:#a6e22e">getShardInfo</span><span style="color:#f92672">(</span><span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span> key<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 针对访问的key生成对应的hash值
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// 根据hash值找到对应的redis节点
</span><span style="color:#75715e"></span>    SortedMap<span style="color:#f92672">&lt;</span>Long<span style="color:#f92672">,</span> S<span style="color:#f92672">&gt;</span> tail <span style="color:#f92672">=</span> nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">tailMap</span><span style="color:#f92672">(</span>algo<span style="color:#f92672">.</span><span style="color:#a6e22e">hash</span><span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>tail<span style="color:#f92672">.</span><span style="color:#a6e22e">isEmpty</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">return</span> nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">firstKey</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
    <span style="color:#66d9ef">return</span> tail<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>tail<span style="color:#f92672">.</span><span style="color:#a6e22e">firstKey</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h2 id="六jedis集群模式的访问">六、Jedis集群模式的访问</h2>
<p>基于前面介绍的Redis的集群原理来理解Jedis的集群模式的访问。</p>
<p>Jedis能够实现key和哈希槽的定位的核心机制在于哈希槽和Redis节点的映射，而这个发现过程基于Redis的cluster slot命令。</p>
<p>关于Redis集群操作的命令：Redis通过cluster slots会返回Redis集群的整体状况。返回每一个Redis节点的信息包含：</p>
<ul>
<li>哈希槽起始编号</li>
<li>哈希槽结束编号</li>
<li>哈希槽对应master节点，节点使用IP/Port表示</li>
<li>master节点的第一个副本</li>
<li>master节点的第二个副本</li>
</ul>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:30001&gt; cluster slots
1<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">0</span> // 开始槽位
   2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">5460</span> // 结束槽位
   3<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span> // master节点的host
      2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">30001</span> // master节点的port
      3<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;09dbe9720cda62f7865eabc5fd8857c5d2678366&#34;</span> // 节点的编码
   4<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span> // slave节点的host
      2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">30004</span> // slave节点的port
      3<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;821d8ca00d7ccf931ed3ffc7e3db0599d2271abf&#34;</span> // 节点的编码
2<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">5461</span>
   2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">10922</span>
   3<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span>
      2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">30002</span>
      3<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;c9d93d9f2c0c524ff34cc11838c2003d8c29e013&#34;</span>
   4<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span>
      2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">30005</span>
      3<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;faadb3eb99009de4ab72ad6b6ed87634c7ee410f&#34;</span>
3<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">10923</span>
   2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">16383</span>
   3<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span>
      2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">30003</span>
      3<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;044ec91f325b7595e76dbcb18cc688b6a5b434a1&#34;</span>
   4<span style="color:#f92672">)</span> 1<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span>
      2<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>integer<span style="color:#f92672">)</span> <span style="color:#ae81ff">30006</span>
      3<span style="color:#f92672">)</span> <span style="color:#e6db74">&#34;58e6e48d41228013e5d9c1c37c5060693925e97e&#34;</span>
</code></pre></div><p>Jedis访问集群模式Redis的整体流程图如下所示，从图中可以看出核心的流程包含JedisCluster对象的创建以及通过JedisCluster对象实现Redis的访问。</p>
<p>JedisCluster对象的创建核心在于创建JedisClusterInfoCache对象并通过集群发现来建立slot和集群节点的映射关系。</p>
<p>JedisCluster对Redis集群的访问在于获取key所在的Redis节点并通过Jedis对象进行访问。</p>
<!-- raw HTML omitted -->
<h3 id="61-创建过程">6.1 创建过程</h3>
<p>JedisCluster的类关系如下图所示，在图中可以看到核心变量JedisSlotBasedConnectionHandler对象。</p>
<!-- raw HTML omitted -->
<p>JedisCluster的父类BinaryJedisCluster创建了JedisSlotBasedConnectionHandler对象，该对象负责和Redis的集群进行通信。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisCluster</span> <span style="color:#66d9ef">extends</span> BinaryJedisCluster <span style="color:#66d9ef">implements</span> JedisClusterCommands<span style="color:#f92672">,</span>
    MultiKeyJedisClusterCommands<span style="color:#f92672">,</span> JedisClusterScriptingCommands <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">JedisCluster</span><span style="color:#f92672">(</span>Set<span style="color:#f92672">&lt;</span>HostAndPort<span style="color:#f92672">&gt;</span> jedisClusterNode<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> connectionTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> soTimeout<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">int</span> maxAttempts<span style="color:#f92672">,</span> String password<span style="color:#f92672">,</span> String clientName<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> GenericObjectPoolConfig poolConfig<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">boolean</span> ssl<span style="color:#f92672">,</span> SSLSocketFactory sslSocketFactory<span style="color:#f92672">,</span> SSLParameters sslParameters<span style="color:#f92672">,</span>
      HostnameVerifier hostnameVerifier<span style="color:#f92672">,</span> JedisClusterHostAndPortMap hostAndPortMap<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
    <span style="color:#75715e">// 访问父类BinaryJedisCluster
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span><span style="color:#f92672">(</span>jedisClusterNode<span style="color:#f92672">,</span> connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span> maxAttempts<span style="color:#f92672">,</span> password<span style="color:#f92672">,</span> clientName<span style="color:#f92672">,</span> poolConfig<span style="color:#f92672">,</span>
        ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">,</span> hostAndPortMap<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BinaryJedisCluster</span> <span style="color:#66d9ef">implements</span> BinaryJedisClusterCommands<span style="color:#f92672">,</span>
    MultiKeyBinaryJedisClusterCommands<span style="color:#f92672">,</span> JedisClusterBinaryScriptingCommands<span style="color:#f92672">,</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">BinaryJedisCluster</span><span style="color:#f92672">(</span>Set<span style="color:#f92672">&lt;</span>HostAndPort<span style="color:#f92672">&gt;</span> jedisClusterNode<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> connectionTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> soTimeout<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">int</span> maxAttempts<span style="color:#f92672">,</span> String user<span style="color:#f92672">,</span> String password<span style="color:#f92672">,</span> String clientName<span style="color:#f92672">,</span> GenericObjectPoolConfig poolConfig<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">boolean</span> ssl<span style="color:#f92672">,</span> SSLSocketFactory sslSocketFactory<span style="color:#f92672">,</span> SSLParameters sslParameters<span style="color:#f92672">,</span>
      HostnameVerifier hostnameVerifier<span style="color:#f92672">,</span> JedisClusterHostAndPortMap hostAndPortMap<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
    <span style="color:#75715e">// 创建JedisSlotBasedConnectionHandler对象
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">connectionHandler</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> JedisSlotBasedConnectionHandler<span style="color:#f92672">(</span>jedisClusterNode<span style="color:#f92672">,</span> poolConfig<span style="color:#f92672">,</span>
        connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span> user<span style="color:#f92672">,</span> password<span style="color:#f92672">,</span> clientName<span style="color:#f92672">,</span> ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">,</span> hostAndPortMap<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">maxAttempts</span> <span style="color:#f92672">=</span> maxAttempts<span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>JedisSlotBasedConnectionHandler的核心在于创建并初始化JedisClusterInfoCache对象，该对象缓存了Redis集群的信息。</p>
<p>JedisClusterInfoCache对象的初始化过程通过initializeSlotsCache来完成，主要目的用于实现集群节点和槽位发现。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisSlotBasedConnectionHandler</span> <span style="color:#66d9ef">extends</span> JedisClusterConnectionHandler <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">JedisSlotBasedConnectionHandler</span><span style="color:#f92672">(</span>Set<span style="color:#f92672">&lt;</span>HostAndPort<span style="color:#f92672">&gt;</span> nodes<span style="color:#f92672">,</span> GenericObjectPoolConfig poolConfig<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">int</span> connectionTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> soTimeout<span style="color:#f92672">,</span> String user<span style="color:#f92672">,</span> String password<span style="color:#f92672">,</span> String clientName<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">boolean</span> ssl<span style="color:#f92672">,</span> SSLSocketFactory sslSocketFactory<span style="color:#f92672">,</span> SSLParameters sslParameters<span style="color:#f92672">,</span>
      HostnameVerifier hostnameVerifier<span style="color:#f92672">,</span> JedisClusterHostAndPortMap portMap<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
    <span style="color:#66d9ef">super</span><span style="color:#f92672">(</span>nodes<span style="color:#f92672">,</span> poolConfig<span style="color:#f92672">,</span> connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span> user<span style="color:#f92672">,</span> password<span style="color:#f92672">,</span> clientName<span style="color:#f92672">,</span>
        ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">,</span> portMap<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisClusterConnectionHandler</span> <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">JedisClusterConnectionHandler</span><span style="color:#f92672">(</span>Set<span style="color:#f92672">&lt;</span>HostAndPort<span style="color:#f92672">&gt;</span> nodes<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> GenericObjectPoolConfig poolConfig<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">int</span> connectionTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> soTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> infiniteSoTimeout<span style="color:#f92672">,</span> String user<span style="color:#f92672">,</span> String password<span style="color:#f92672">,</span> String clientName<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">boolean</span> ssl<span style="color:#f92672">,</span> SSLSocketFactory sslSocketFactory<span style="color:#f92672">,</span> SSLParameters sslParameters<span style="color:#f92672">,</span>
      HostnameVerifier hostnameVerifier<span style="color:#f92672">,</span> JedisClusterHostAndPortMap portMap<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
    <span style="color:#75715e">// 创建JedisClusterInfoCache对象
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">cache</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> JedisClusterInfoCache<span style="color:#f92672">(</span>poolConfig<span style="color:#f92672">,</span> connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span> infiniteSoTimeout<span style="color:#f92672">,</span>
        user<span style="color:#f92672">,</span> password<span style="color:#f92672">,</span> clientName<span style="color:#f92672">,</span> ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">,</span> portMap<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    <span style="color:#75715e">// 初始化jedis的Slot信息
</span><span style="color:#75715e"></span>    initializeSlotsCache<span style="color:#f92672">(</span>nodes<span style="color:#f92672">,</span> connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span> infiniteSoTimeout<span style="color:#f92672">,</span>
        user<span style="color:#f92672">,</span> password<span style="color:#f92672">,</span> clientName<span style="color:#f92672">,</span> ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
 
 
  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">initializeSlotsCache</span><span style="color:#f92672">(</span>Set<span style="color:#f92672">&lt;</span>HostAndPort<span style="color:#f92672">&gt;</span> startNodes<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">int</span> connectionTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> soTimeout<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> infiniteSoTimeout<span style="color:#f92672">,</span> String user<span style="color:#f92672">,</span> String password<span style="color:#f92672">,</span> String clientName<span style="color:#f92672">,</span>
      <span style="color:#66d9ef">boolean</span> ssl<span style="color:#f92672">,</span> SSLSocketFactory sslSocketFactory<span style="color:#f92672">,</span> SSLParameters sslParameters<span style="color:#f92672">,</span> HostnameVerifier hostnameVerifier<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span>HostAndPort hostAndPort <span style="color:#f92672">:</span> startNodes<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
      <span style="color:#66d9ef">try</span> <span style="color:#f92672">(</span>Jedis jedis <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Jedis<span style="color:#f92672">(</span>hostAndPort<span style="color:#f92672">.</span><span style="color:#a6e22e">getHost</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> hostAndPort<span style="color:#f92672">.</span><span style="color:#a6e22e">getPort</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> connectionTimeout<span style="color:#f92672">,</span>
          soTimeout<span style="color:#f92672">,</span> infiniteSoTimeout<span style="color:#f92672">,</span> ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">)</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
        <span style="color:#75715e">// 通过discoverClusterNodesAndSlots进行集群发现
</span><span style="color:#75715e"></span>        cache<span style="color:#f92672">.</span><span style="color:#a6e22e">discoverClusterNodesAndSlots</span><span style="color:#f92672">(</span>jedis<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#66d9ef">return</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisConnectionException e<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>JedisClusterInfoCache的nodes用来保存Redis集群的节点信息，slots用来保存槽位和集群节点的信息。</p>
<p>nodes和slots维持的对象都是JedisPool对象，该对象维持了和Redis的连接信息。集群的发现过程由discoverClusterNodesAndSlots来实现，本质是执行Redis的集群发现命令cluster slots实现的。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisClusterInfoCache</span> <span style="color:#f92672">{</span>
  <span style="color:#75715e">// 负责保存redis集群的节点信息
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">final</span> Map<span style="color:#f92672">&lt;</span>String<span style="color:#f92672">,</span> JedisPool<span style="color:#f92672">&gt;</span> nodes <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashMap<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#75715e">// 负责保存redis的槽位和redis节点的映射关系
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">final</span> Map<span style="color:#f92672">&lt;</span>Integer<span style="color:#f92672">,</span> JedisPool<span style="color:#f92672">&gt;</span> slots <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashMap<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
  <span style="color:#75715e">// 负责集群的发现逻辑
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">discoverClusterNodesAndSlots</span><span style="color:#f92672">(</span>Jedis jedis<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    w<span style="color:#f92672">.</span><span style="color:#a6e22e">lock</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
      reset<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> slots <span style="color:#f92672">=</span> jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">clusterSlots</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
      <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span>Object slotInfoObj <span style="color:#f92672">:</span> slots<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> slotInfo <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span><span style="color:#f92672">)</span> slotInfoObj<span style="color:#f92672">;</span>
 
        <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>slotInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">size</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">&lt;</span><span style="color:#f92672">=</span> MASTER_NODE_INDEX<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
          <span style="color:#66d9ef">continue</span><span style="color:#f92672">;</span>
        <span style="color:#f92672">}</span>
        <span style="color:#75715e">// 获取redis节点对应的槽位信息
</span><span style="color:#75715e"></span>        List<span style="color:#f92672">&lt;</span>Integer<span style="color:#f92672">&gt;</span> slotNums <span style="color:#f92672">=</span> getAssignedSlotArray<span style="color:#f92672">(</span>slotInfo<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
        <span style="color:#75715e">// hostInfos
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">int</span> size <span style="color:#f92672">=</span> slotInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">size</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> MASTER_NODE_INDEX<span style="color:#f92672">;</span> i <span style="color:#f92672">&lt;</span> size<span style="color:#f92672">;</span> i<span style="color:#f92672">+</span><span style="color:#f92672">+</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
          List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> hostInfos <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span><span style="color:#f92672">)</span> slotInfo<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>i<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
          <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>hostInfos<span style="color:#f92672">.</span><span style="color:#a6e22e">isEmpty</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
            <span style="color:#66d9ef">continue</span><span style="color:#f92672">;</span>
          <span style="color:#f92672">}</span>
 
          HostAndPort targetNode <span style="color:#f92672">=</span> generateHostAndPort<span style="color:#f92672">(</span>hostInfos<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
          <span style="color:#75715e">// 负责保存redis节点信息
</span><span style="color:#75715e"></span>          setupNodeIfNotExist<span style="color:#f92672">(</span>targetNode<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
          <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>i <span style="color:#f92672">=</span><span style="color:#f92672">=</span> MASTER_NODE_INDEX<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
            <span style="color:#75715e">// 负责保存槽位和redis节点的映射关系
</span><span style="color:#75715e"></span>            assignSlotsToNode<span style="color:#f92672">(</span>slotNums<span style="color:#f92672">,</span> targetNode<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
          <span style="color:#f92672">}</span>
        <span style="color:#f92672">}</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">finally</span> <span style="color:#f92672">{</span>
      w<span style="color:#f92672">.</span><span style="color:#a6e22e">unlock</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">assignSlotsToNode</span><span style="color:#f92672">(</span>List<span style="color:#f92672">&lt;</span>Integer<span style="color:#f92672">&gt;</span> targetSlots<span style="color:#f92672">,</span> HostAndPort targetNode<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    w<span style="color:#f92672">.</span><span style="color:#a6e22e">lock</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
      JedisPool targetPool <span style="color:#f92672">=</span> setupNodeIfNotExist<span style="color:#f92672">(</span>targetNode<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#75715e">// 保存槽位和对应的JedisPool对象
</span><span style="color:#75715e"></span>      <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span>Integer slot <span style="color:#f92672">:</span> targetSlots<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        slots<span style="color:#f92672">.</span><span style="color:#a6e22e">put</span><span style="color:#f92672">(</span>slot<span style="color:#f92672">,</span> targetPool<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">finally</span> <span style="color:#f92672">{</span>
      w<span style="color:#f92672">.</span><span style="color:#a6e22e">unlock</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
 
  <span style="color:#66d9ef">public</span> JedisPool <span style="color:#a6e22e">setupNodeIfNotExist</span><span style="color:#f92672">(</span>HostAndPort node<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    w<span style="color:#f92672">.</span><span style="color:#a6e22e">lock</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// 生产redis节点对应的nodeKey
</span><span style="color:#75715e"></span>      String nodeKey <span style="color:#f92672">=</span> getNodeKey<span style="color:#f92672">(</span>node<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      JedisPool existingPool <span style="color:#f92672">=</span> nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>nodeKey<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>existingPool <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#66d9ef">return</span> existingPool<span style="color:#f92672">;</span>
      <span style="color:#75715e">// 生产redis节点对应的JedisPool
</span><span style="color:#75715e"></span>      JedisPool nodePool <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> JedisPool<span style="color:#f92672">(</span>poolConfig<span style="color:#f92672">,</span> node<span style="color:#f92672">.</span><span style="color:#a6e22e">getHost</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> node<span style="color:#f92672">.</span><span style="color:#a6e22e">getPort</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span>
          connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span> infiniteSoTimeout<span style="color:#f92672">,</span> user<span style="color:#f92672">,</span> password<span style="color:#f92672">,</span> 0<span style="color:#f92672">,</span> clientName<span style="color:#f92672">,</span>
          ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#75715e">// 保存redis节点的key和对应的JedisPool对象
</span><span style="color:#75715e"></span>      nodes<span style="color:#f92672">.</span><span style="color:#a6e22e">put</span><span style="color:#f92672">(</span>nodeKey<span style="color:#f92672">,</span> nodePool<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">return</span> nodePool<span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">finally</span> <span style="color:#f92672">{</span>
      w<span style="color:#f92672">.</span><span style="color:#a6e22e">unlock</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>JedisPool的类关系如下图所示，其中内部internalPool是通过apache common pool来实现的池化。</p>
<!-- raw HTML omitted -->
<p>JedisPool内部的internalPool通过JedisFactory的makeObject来创建Jedis对象。</p>
<p>每个Redis节点都会对应一个JedisPool对象，通过JedisPool来管理Jedis的申请释放复用等。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisPool</span> <span style="color:#66d9ef">extends</span> JedisPoolAbstract <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">JedisPool</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">this</span><span style="color:#f92672">(</span>Protocol<span style="color:#f92672">.</span><span style="color:#a6e22e">DEFAULT_HOST</span><span style="color:#f92672">,</span> Protocol<span style="color:#f92672">.</span><span style="color:#a6e22e">DEFAULT_PORT</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisPoolAbstract</span> <span style="color:#66d9ef">extends</span> Pool<span style="color:#f92672">&lt;</span>Jedis<span style="color:#f92672">&gt;</span> <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">JedisPoolAbstract</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">super</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Pool</span><span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">protected</span> GenericObjectPool<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> internalPool<span style="color:#f92672">;</span>
 
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">initPool</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> GenericObjectPoolConfig poolConfig<span style="color:#f92672">,</span> PooledObjectFactory<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> factory<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">internalPool</span> <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
        closeInternalPool<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>Exception e<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span>
    <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">internalPool</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> GenericObjectPool<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span>factory<span style="color:#f92672">,</span> poolConfig<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisFactory</span> <span style="color:#66d9ef">implements</span> PooledObjectFactory<span style="color:#f92672">&lt;</span>Jedis<span style="color:#f92672">&gt;</span> <span style="color:#f92672">{</span>
   
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> PooledObject<span style="color:#f92672">&lt;</span>Jedis<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">makeObject</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#66d9ef">throws</span> Exception <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 创建Jedis对象
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">final</span> HostAndPort hp <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">hostAndPort</span><span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">final</span> Jedis jedis <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Jedis<span style="color:#f92672">(</span>hp<span style="color:#f92672">.</span><span style="color:#a6e22e">getHost</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> hp<span style="color:#f92672">.</span><span style="color:#a6e22e">getPort</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> connectionTimeout<span style="color:#f92672">,</span> soTimeout<span style="color:#f92672">,</span>
        infiniteSoTimeout<span style="color:#f92672">,</span> ssl<span style="color:#f92672">,</span> sslSocketFactory<span style="color:#f92672">,</span> sslParameters<span style="color:#f92672">,</span> hostnameVerifier<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// Jedis对象连接
</span><span style="color:#75715e"></span>      jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">connect</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>user <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">auth</span><span style="color:#f92672">(</span>user<span style="color:#f92672">,</span> password<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>password <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">auth</span><span style="color:#f92672">(</span>password<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>database <span style="color:#f92672">!</span><span style="color:#f92672">=</span> 0<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">select</span><span style="color:#f92672">(</span>database<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>clientName <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">clientSetname</span><span style="color:#f92672">(</span>clientName<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisException je<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">close</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">throw</span> je<span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
    <span style="color:#75715e">// 将Jedis对象包装成DefaultPooledObject进行返回
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> DefaultPooledObject<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span>jedis<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h3 id="62-访问过程">6.2 访问过程</h3>
<p>JedisCluster访问Redis的过程通过JedisClusterCommand来实现重试机制，最终通过Jedis对象来实现访问。</p>
<p>从实现的角度来说JedisCluster是在Jedis之上封装了一层，进行集群节点定位以及重试机制等。</p>
<p>以set命令为例，整个访问通过JedisClusterCommand实现如下：</p>
<ul>
<li>计算key所在的Redis节点。</li>
<li>获取Redis节点对应的Jedis对象。</li>
<li>通过Jedis对象进行set操作。</li>
</ul>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisCluster</span> <span style="color:#66d9ef">extends</span> BinaryJedisCluster <span style="color:#66d9ef">implements</span> JedisClusterCommands<span style="color:#f92672">,</span>
    MultiKeyJedisClusterCommands<span style="color:#f92672">,</span> JedisClusterScriptingCommands <span style="color:#f92672">{</span>
 
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String key<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> String value<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> SetParams params<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> JedisClusterCommand<span style="color:#f92672">&lt;</span>String<span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span>connectionHandler<span style="color:#f92672">,</span> maxAttempts<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#a6e22e">@Override</span>
      <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">execute</span><span style="color:#f92672">(</span>Jedis connection<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        <span style="color:#66d9ef">return</span> connection<span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span>key<span style="color:#f92672">,</span> value<span style="color:#f92672">,</span> params<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span><span style="color:#f92672">.</span><span style="color:#a6e22e">run</span><span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>JedisClusterCommand的run方法核心主要定位Redis的key所在的Redis节点，然后获取与该节点对应的Jedis对象进行访问。</p>
<p>在Jedis对象访问异常后，JedisClusterCommand会进行重试操作并按照一定策略执行renewSlotCache方法进行重集群节点重发现动作。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java">
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisClusterCommand</span><span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> T <span style="color:#a6e22e">run</span><span style="color:#f92672">(</span>String key<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 针对key进行槽位的计算
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> runWithRetries<span style="color:#f92672">(</span>JedisClusterCRC16<span style="color:#f92672">.</span><span style="color:#a6e22e">getSlot</span><span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">,</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">maxAttempts</span><span style="color:#f92672">,</span> <span style="color:#66d9ef">false</span><span style="color:#f92672">,</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
   
  <span style="color:#66d9ef">private</span> T <span style="color:#a6e22e">runWithRetries</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> slot<span style="color:#f92672">,</span> <span style="color:#66d9ef">int</span> attempts<span style="color:#f92672">,</span> <span style="color:#66d9ef">boolean</span> tryRandomNode<span style="color:#f92672">,</span> JedisRedirectionException redirect<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
    Jedis connection <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
 
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>redirect <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        connection <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">connectionHandler</span><span style="color:#f92672">.</span><span style="color:#a6e22e">getConnectionFromNode</span><span style="color:#f92672">(</span>redirect<span style="color:#f92672">.</span><span style="color:#a6e22e">getTargetNode</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>redirect <span style="color:#66d9ef">instanceof</span> JedisAskDataException<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
          connection<span style="color:#f92672">.</span><span style="color:#a6e22e">asking</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#f92672">}</span>
      <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">{</span>
        <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>tryRandomNode<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
          connection <span style="color:#f92672">=</span> connectionHandler<span style="color:#f92672">.</span><span style="color:#a6e22e">getConnection</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">{</span>
          <span style="color:#75715e">// 根据slot去获取Jedis对象
</span><span style="color:#75715e"></span>          connection <span style="color:#f92672">=</span> connectionHandler<span style="color:#f92672">.</span><span style="color:#a6e22e">getConnectionFromSlot</span><span style="color:#f92672">(</span>slot<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#f92672">}</span>
      <span style="color:#f92672">}</span>
      <span style="color:#75715e">// 执行真正的Redis的命令
</span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> execute<span style="color:#f92672">(</span>connection<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisNoReachableClusterNodeException jnrcne<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">throw</span> jnrcne<span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisConnectionException jce<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
 
      releaseConnection<span style="color:#f92672">(</span>connection<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      connection <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">;</span>
 
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>attempts <span style="color:#f92672">&lt;</span><span style="color:#f92672">=</span> 1<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        <span style="color:#75715e">// 保证最后两次机会去重新刷新槽位和节点的对应的信息
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">connectionHandler</span><span style="color:#f92672">.</span><span style="color:#a6e22e">renewSlotCache</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
      <span style="color:#75715e">// 按照重试次数进行重试操作
</span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> runWithRetries<span style="color:#f92672">(</span>slot<span style="color:#f92672">,</span> attempts <span style="color:#f92672">-</span> 1<span style="color:#f92672">,</span> tryRandomNode<span style="color:#f92672">,</span> redirect<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisRedirectionException jre<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// 针对返回Move命令立即触发重新刷新槽位和节点的对应信息
</span><span style="color:#75715e"></span>      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>jre <span style="color:#66d9ef">instanceof</span> JedisMovedDataException<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        <span style="color:#75715e">// it rebuilds cluster&#39;s slot cache recommended by Redis cluster specification
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">this</span><span style="color:#f92672">.</span><span style="color:#a6e22e">connectionHandler</span><span style="color:#f92672">.</span><span style="color:#a6e22e">renewSlotCache</span><span style="color:#f92672">(</span>connection<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
 
      releaseConnection<span style="color:#f92672">(</span>connection<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      connection <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">;</span>
 
      <span style="color:#66d9ef">return</span> runWithRetries<span style="color:#f92672">(</span>slot<span style="color:#f92672">,</span> attempts <span style="color:#f92672">-</span> 1<span style="color:#f92672">,</span> <span style="color:#66d9ef">false</span><span style="color:#f92672">,</span> jre<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">finally</span> <span style="color:#f92672">{</span>
      releaseConnection<span style="color:#f92672">(</span>connection<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>JedisSlotBasedConnectionHandler的cache对象维持了slot和node的映射关系，通过getConnectionFromSlot方法来获取该slot对应的Jedis对象。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java">
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JedisSlotBasedConnectionHandler</span> <span style="color:#66d9ef">extends</span> JedisClusterConnectionHandler <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">final</span> JedisClusterInfoCache cache<span style="color:#f92672">;</span>
 
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> Jedis <span style="color:#a6e22e">getConnectionFromSlot</span><span style="color:#f92672">(</span><span style="color:#66d9ef">int</span> slot<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 获取槽位对应的JedisPool对象
</span><span style="color:#75715e"></span>    JedisPool connectionPool <span style="color:#f92672">=</span> cache<span style="color:#f92672">.</span><span style="color:#a6e22e">getSlotPool</span><span style="color:#f92672">(</span>slot<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>connectionPool <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// 从JedisPool对象中获取Jedis对象
</span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> connectionPool<span style="color:#f92672">.</span><span style="color:#a6e22e">getResource</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// 获取失败就重新刷新槽位信息
</span><span style="color:#75715e"></span>      renewSlotCache<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      connectionPool <span style="color:#f92672">=</span> cache<span style="color:#f92672">.</span><span style="color:#a6e22e">getSlotPool</span><span style="color:#f92672">(</span>slot<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>connectionPool <span style="color:#f92672">!</span><span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        <span style="color:#66d9ef">return</span> connectionPool<span style="color:#f92672">.</span><span style="color:#a6e22e">getResource</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">{</span>
        <span style="color:#75715e">//no choice, fallback to new connection to random node
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> getConnection<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#f92672">}</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h2 id="七jedis的pipeline实现">七、Jedis的Pipeline实现</h2>
<p>Pipeline的技术核心思想是将多个命令发送到服务器而不用等待回复，最后在一个步骤中读取该答复。这种模式的好处在于节省了请求响应这种模式的网络开销。</p>
<p>Redis的普通命令如set和Pipeline批量操作的核心的差别在于set命令的操作会直接发送请求到Redis并同步等待结果返回，而Pipeline的操作会发送请求但不立即同步等待结果返回，具体的实现可以从Jedis的源码一探究竟。</p>
<p>原生的Pipeline在集群模式下相关的key必须Hash到同一个节点才能生效，原因在于Pipeline下的Client对象只能其中的一个节点建立了连接。</p>
<p>在集群模式下归属于不同节点的key能够使用Pipeline就需要针对每个key保存对应的节点的client对象，在最后执行获取数据的时候一并获取。本质上可以认为在单节点的Pipeline的基础上封装成一个集群式的Pipeline。</p>
<h3 id="71-pipeline用法分析">7.1 Pipeline用法分析</h3>
<p>Pipeline访问单节点的Redis的时候，通过Jedis对象的Pipeline方法返回Pipeline对象，其他的命令操作通过该Pipeline对象进行访问。</p>
<p>Pipeline从使用角度来分析，会批量发送多个命令并最后统一使用syncAndReturnAll来一次性返回结果。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">pipeline</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    jedis <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Jedis<span style="color:#f92672">(</span>hnp<span style="color:#f92672">.</span><span style="color:#a6e22e">getHost</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> hnp<span style="color:#f92672">.</span><span style="color:#a6e22e">getPort</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">,</span> 500<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    Pipeline p <span style="color:#f92672">=</span> jedis<span style="color:#f92672">.</span><span style="color:#a6e22e">pipelined</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 批量发送命令到redis
</span><span style="color:#75715e"></span>    p<span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;foo&#34;</span><span style="color:#f92672">,</span> <span style="color:#e6db74">&#34;bar&#34;</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    p<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;foo&#34;</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 同步等待响应结果
</span><span style="color:#75715e"></span>    List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> results <span style="color:#f92672">=</span> p<span style="color:#f92672">.</span><span style="color:#a6e22e">syncAndReturnAll</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 
    assertEquals<span style="color:#f92672">(</span>2<span style="color:#f92672">,</span> results<span style="color:#f92672">.</span><span style="color:#a6e22e">size</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    assertEquals<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;OK&#34;</span><span style="color:#f92672">,</span> results<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>0<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    assertEquals<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;bar&#34;</span><span style="color:#f92672">,</span> results<span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span>1<span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
 <span style="color:#f92672">}</span>
 
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">PipelineBase</span> <span style="color:#66d9ef">extends</span> Queable <span style="color:#66d9ef">implements</span> BinaryRedisPipeline<span style="color:#f92672">,</span> RedisPipeline <span style="color:#f92672">{</span>
 
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> Response<span style="color:#f92672">&lt;</span>String<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> String key<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> String value<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 发送命令
</span><span style="color:#75715e"></span>    getClient<span style="color:#f92672">(</span>key<span style="color:#f92672">)</span><span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span>key<span style="color:#f92672">,</span> value<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// pipeline的getResponse只是把待响应的请求聚合到pipelinedResponses对象当中
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> getResponse<span style="color:#f92672">(</span>BuilderFactory<span style="color:#f92672">.</span><span style="color:#a6e22e">STRING</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Queable</span> <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">private</span> Queue<span style="color:#f92672">&lt;</span>Response<span style="color:#f92672">&lt;</span><span style="color:#f92672">?</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">&gt;</span> pipelinedResponses <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> LinkedList<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#66d9ef">protected</span> <span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> Response<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">getResponse</span><span style="color:#f92672">(</span>Builder<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> builder<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    Response<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> lr <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Response<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span>builder<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 统一保存到响应队列当中
</span><span style="color:#75715e"></span>    pipelinedResponses<span style="color:#f92672">.</span><span style="color:#a6e22e">add</span><span style="color:#f92672">(</span>lr<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">return</span> lr<span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Pipeline</span> <span style="color:#66d9ef">extends</span> MultiKeyPipelineBase <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
 
  <span style="color:#66d9ef">public</span> List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">syncAndReturnAll</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span>getPipelinedResponseLength<span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">&gt;</span> 0<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// 根据批量发送命令的个数即需要批量返回命令的个数，通过client对象进行批量读取
</span><span style="color:#75715e"></span>      List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> unformatted <span style="color:#f92672">=</span> client<span style="color:#f92672">.</span><span style="color:#a6e22e">getMany</span><span style="color:#f92672">(</span>getPipelinedResponseLength<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      List<span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> formatted <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ArrayList<span style="color:#f92672">&lt;</span><span style="color:#f92672">&gt;</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">for</span> <span style="color:#f92672">(</span>Object o <span style="color:#f92672">:</span> unformatted<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
        <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
          <span style="color:#75715e">// 格式化每个返回的结果并最终保存在列表中进行返回
</span><span style="color:#75715e"></span>          formatted<span style="color:#f92672">.</span><span style="color:#a6e22e">add</span><span style="color:#f92672">(</span>generateResponse<span style="color:#f92672">(</span>o<span style="color:#f92672">)</span><span style="color:#f92672">.</span><span style="color:#a6e22e">get</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>JedisDataException e<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
          formatted<span style="color:#f92672">.</span><span style="color:#a6e22e">add</span><span style="color:#f92672">(</span>e<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
        <span style="color:#f92672">}</span>
      <span style="color:#f92672">}</span>
      <span style="color:#66d9ef">return</span> formatted<span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">return</span> java<span style="color:#f92672">.</span><span style="color:#a6e22e">util</span><span style="color:#f92672">.</span><span style="color:#a6e22e">Collections</span><span style="color:#f92672">.</span><span style="color:#f92672">&lt;</span>Object<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">emptyList</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><p>普通set命令发送请求给Redis后立即通过getStatusCodeReply来获取响应结果，所以这是一种请求响应的模式。getStatusCodeReply在获取响应结果的时候会通过flush()命令强制发送报文到Redis服务端然后通过读取响应结果。</p>
<div class="highlight"><pre style="color:#f8f8f2;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-java" data-lang="java"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BinaryJedis</span> <span style="color:#66d9ef">implements</span> BasicCommands<span style="color:#f92672">,</span> BinaryJedisCommands<span style="color:#f92672">,</span> MultiKeyBinaryCommands<span style="color:#f92672">,</span>
    AdvancedBinaryJedisCommands<span style="color:#f92672">,</span> BinaryScriptingCommands<span style="color:#f92672">,</span> Closeable <span style="color:#f92672">{</span>
 
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">set</span><span style="color:#f92672">(</span><span style="color:#66d9ef">final</span> <span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span> key<span style="color:#f92672">,</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span> value<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    checkIsInMultiOrPipeline<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 发送命令
</span><span style="color:#75715e"></span>    client<span style="color:#f92672">.</span><span style="color:#a6e22e">set</span><span style="color:#f92672">(</span>key<span style="color:#f92672">,</span> value<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 等待请求响应
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> client<span style="color:#f92672">.</span><span style="color:#a6e22e">getStatusCodeReply</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Connection</span> <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">getStatusCodeReply</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#75715e">// 通过flush立即发送请求
</span><span style="color:#75715e"></span>    flush<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#75715e">// 处理响应请求
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span> resp <span style="color:#f92672">=</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">byte</span><span style="color:#f92672">[</span><span style="color:#f92672">]</span><span style="color:#f92672">)</span> readProtocolWithCheckingBroken<span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#66d9ef">if</span> <span style="color:#f92672">(</span><span style="color:#66d9ef">null</span> <span style="color:#f92672">=</span><span style="color:#f92672">=</span> resp<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">else</span> <span style="color:#f92672">{</span>
      <span style="color:#66d9ef">return</span> SafeEncoder<span style="color:#f92672">.</span><span style="color:#a6e22e">encode</span><span style="color:#f92672">(</span>resp<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
 
 
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Connection</span> <span style="color:#66d9ef">implements</span> Closeable <span style="color:#f92672">{</span>
  <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">flush</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">try</span> <span style="color:#f92672">{</span>
      <span style="color:#75715e">// 针对输出流进行flush操作保证报文的发出
</span><span style="color:#75715e"></span>      outputStream<span style="color:#f92672">.</span><span style="color:#a6e22e">flush</span><span style="color:#f92672">(</span><span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span> <span style="color:#66d9ef">catch</span> <span style="color:#f92672">(</span>IOException ex<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      broken <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span><span style="color:#f92672">;</span>
      <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> JedisConnectionException<span style="color:#f92672">(</span>ex<span style="color:#f92672">)</span><span style="color:#f92672">;</span>
    <span style="color:#f92672">}</span>
  <span style="color:#f92672">}</span>
<span style="color:#f92672">}</span>
</code></pre></div><h2 id="八结束语">八、结束语</h2>
<p>Jedis作为Redis官方首选的Java客户端开发包，支持绝大部分的Redis的命令，也是日常中使用较多的Redis客户端。</p>
<p>了解了Jedis的实现原理，除了能够支持Redis的日常操作外，还能更好的应对Redis的额外操作诸如扩容时的技术选型。</p>
<p>通过介绍Jedis针对单机模式、分配模式、集群模式三种场景访问方式，让大家有个从宏观到微观的理解过程，掌握Jedis的核心思想并更好的应用到实践当中。</p>

    </div>
    <footer class="post-footer">
     

     <div class="post-nav">
    <div class="post-nav-next post-nav-item">
    
        <a href="http://www.shutdown.cn/post/%E4%B8%8B%E8%BD%BDjtopo-demo%E4%BB%A3%E7%A0%81/" rel="next" title="">
        <i class="fa fa-chevron-left"></i> 
        </a>
    
    </div>

    <div class="post-nav-prev post-nav-item">
    
    </div>
</div>
      
     
     
     






    </footer>
  </article>
</section>

          </div>
        </div>
        <div class="sidebar-toggle">
  <div class="sidebar-toggle-line-wrap">
    <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
  </div>
</div>
<aside id="sidebar" class="sidebar">
  <div class="sidebar-inner">

    <section class="site-overview sidebar-panel  sidebar-panel-active ">
      <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
        src="http://www.shutdown.cn/img/author.jpg"
        alt="不与天斗Domino" />
    <p class="site-author-name" itemprop="name">不与天斗Domino</p>
    <p class="site-description motion-element" itemprop="description"> 
        Programmer &amp; Architect</p>
</div>
      <nav class="site-state motion-element">
    <div class="site-state-item site-state-posts">
      <a href="http://www.shutdown.cn/post/">
        <span class="site-state-item-count">41</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    <div class="site-state-item site-state-categories">    
        <a href="http://www.shutdown.cn/categories/">      
         
        <span class="site-state-item-count">4</span>
        
        <span class="site-state-item-name">分类</span>
        
        </a>
    </div>

    <div class="site-state-item site-state-tags">
        <a href="http://www.shutdown.cn/tags/">
         
        <span class="site-state-item-count">5</span>
        
        <span class="site-state-item-name">标签</span>
        </a>
    </div>
</nav>
      
      

      

      <div class="links-of-blogroll motion-element inline">
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=&amp;m=0&amp;s=220&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33&amp;bv=35" async="async"></script>
</div>

    </section>
    
  </div>
</aside>

      </div>
    </main>
   
    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  <span itemprop="copyrightYear">  &copy; 
  2009 - 2022</span>
  <span class="with-love"><i class="fa fa-heart"></i></span>
  <span class="author" itemprop="copyrightHolder">天地维杰网</span>
  <span class="icp" itemprop="copyrightHolder"><a href="https://beian.miit.gov.cn/" target="_blank">京ICP备13019191号-1</a></span>
</div>
<div class="powered-by">
  Powered by - <a class="theme-link" href="http://gohugo.io" target="_blank" title="hugo" >Hugo v0.63.2</a>
</div>
<div class="theme-info">
  Theme by - <a class="theme-link" href="https://github.com/xtfly/hugo-theme-next" target="_blank"> NexT
  </a>
</div>


      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span id="scrollpercent"><span>0</span>%</span>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/fastclick/lib/fastclick.min.js?v=1.0.6"></script> 
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="http://www.shutdown.cn/js/vendor/ua-parser-js/dist/ua-parser.min.js?v=0.7.9"></script>

<script src="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.pack.js?v=2.1.5"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/utils.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/motion.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/affix.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/schemes/pisces.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/scrollspy.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/post-details.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/toc.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/bootstrap.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'] ],
      displayMath: [ ['$$','$$'] ],
      processEscapes: true
    },
    "HTML-CSS": { fonts: ["TeX"] }
  });
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML' async></script>
</body>
</html>